package com.coderly.onlinegallerysharingplatform.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.coderly.onlinegallerysharingplatform.common.convention.errorcode.ErrorCode;
import com.coderly.onlinegallerysharingplatform.common.convention.exception.BizException;
import com.coderly.onlinegallerysharingplatform.common.response.Response;
import com.coderly.onlinegallerysharingplatform.enums.SpaceRoleEnum;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.SpaceDO;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.SpaceUserDO;
import com.coderly.onlinegallerysharingplatform.model.dao.entity.UserDO;
import com.coderly.onlinegallerysharingplatform.model.dao.mapper.SpaceUserMapper;
import com.coderly.onlinegallerysharingplatform.model.dto.req.spaceuser.AddSpaceUserReqDTO;
import com.coderly.onlinegallerysharingplatform.model.dto.req.spaceuser.DeleteSpaceUserReqDTO;
import com.coderly.onlinegallerysharingplatform.model.dto.req.spaceuser.EditSpaceUserReqDTO;
import com.coderly.onlinegallerysharingplatform.model.dto.req.spaceuser.GetSpaceUserReqDTO;
import com.coderly.onlinegallerysharingplatform.model.dto.resp.space.SpaceRespDTO;
import com.coderly.onlinegallerysharingplatform.model.dto.resp.spaceuser.SpaceUserRespDTO;
import com.coderly.onlinegallerysharingplatform.model.dto.resp.user.UserRespDTO;
import com.coderly.onlinegallerysharingplatform.service.SpaceService;
import com.coderly.onlinegallerysharingplatform.service.SpaceUserService;
import com.coderly.onlinegallerysharingplatform.service.UserService;
import com.coderly.onlinegallerysharingplatform.toolkit.ExceptionTrowUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 空间成员服务实现类
 * 处理空间成员的增删改查及相关业务逻辑
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class SpaceUserServiceImpl extends ServiceImpl<SpaceUserMapper, SpaceUserDO>
        implements SpaceUserService {

    private final UserService userService; // 用户服务

    @Resource(name = "taskExecutor")
    private Executor taskExecutor; // 异步任务执行器

    @Resource
    @Lazy
    private SpaceService spaceService; // 空间服务


    /**
     * 添加空间成员
     *
     * @param addSpaceUserReqDTO 添加成员请求DTO
     * @return 新增成员ID
     */
    @Override
    public Long addSpaceUser(AddSpaceUserReqDTO addSpaceUserReqDTO) {
        // 参数校验
        ExceptionTrowUtils.throwIf(addSpaceUserReqDTO == null, ErrorCode.PARAMS_ERROR);

        // 转换DTO为DO
        SpaceUserDO spaceUserDO = new SpaceUserDO();
        BeanUtils.copyProperties(addSpaceUserReqDTO, spaceUserDO);

        // 验证成员信息
        validSpaceUser(spaceUserDO, true);

        // 保存成员信息
        boolean result = this.save(spaceUserDO);
        ExceptionTrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);

        // 异步更新空间成员计数
        taskExecutor.execute(() -> {
            boolean update = spaceService.lambdaUpdate()
                    .eq(SpaceDO::getId, spaceUserDO.getSpaceId())
                    .setSql("member_count = member_count + 1")
                    .update();
            ExceptionTrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR);
            log.info("## 异步更新团队成员数目+1完成 ##");
        });

        return spaceUserDO.getId();
    }

    /**
     * 验证空间成员信息
     *
     * @param spaceUserDO 空间成员实体
     * @param isAdd       是否为新增操作
     */
    @Override
    public void validSpaceUser(SpaceUserDO spaceUserDO, boolean isAdd) {
        // 基础参数校验
        ExceptionTrowUtils.throwIf(spaceUserDO == null, ErrorCode.PARAMS_ERROR);
        Long spaceId = spaceUserDO.getSpaceId();
        Long userId = spaceUserDO.getUserId();

        // 新增操作特有校验
        if (isAdd) {
            // 必要字段校验
            ExceptionTrowUtils.throwIf(ObjectUtil.hasEmpty(spaceId, userId), ErrorCode.PARAMS_ERROR);

            // 检查用户和空间是否存在
            checkEntityExist(userId, spaceId);

            // 检查用户是否已在空间中
            checkUserNotInSpace(spaceId, userId);

            // 检查空间成员是否已达上限
            checkMemberReachMax(spaceId);
        }

        // 验证角色类型
        validateSpaceRole(spaceUserDO.getSpaceRole());
    }

    /**
     * 检查用户和空间是否存在
     *
     * @param userId  用户ID
     * @param spaceId 空间ID
     */
    private void checkEntityExist(Long userId, Long spaceId) {
        // 检查用户是否存在
        ExceptionTrowUtils.throwIf(!userService.exist(userId), ErrorCode.NOT_FOUND_ERROR, "用户不存在");

        // 检查空间是否存在
        ExceptionTrowUtils.throwIf(!spaceService.exist(spaceId), ErrorCode.NOT_FOUND_ERROR, "空间不存在");
    }

    /**
     * 检查用户是否已在空间中
     *
     * @param spaceId 空间ID
     * @param userId  用户ID
     */
    private void checkUserNotInSpace(Long spaceId, Long userId) {
        // 查询用户是否已在空间中
        boolean exists = this.lambdaQuery()
                .eq(SpaceUserDO::getSpaceId, spaceId)
                .eq(SpaceUserDO::getUserId, userId)
                .exists();

        // 如果已存在则抛出异常
        ExceptionTrowUtils.throwIf(exists, ErrorCode.OPERATION_ERROR, "用户已在当前空间");
    }

    /**
     * 检查空间成员是否已达上限
     *
     * @param spaceId 空间 ID
     */
    private void checkMemberReachMax(Long spaceId) {
        // 获取空间信息
        SpaceDO spaceDO = spaceService.getById(spaceId);

        // 检查成员数量是否已达上限
        ExceptionTrowUtils.throwIf(
                spaceDO.getMemberCount() >= spaceDO.getMemberMax(),
                ErrorCode.PARAMS_ERROR, "团队成员已达到上限");
    }

    /**
     * 验证空间角色是否合法
     *
     * @param spaceRole 空间角色
     */
    private void validateSpaceRole(String spaceRole) {
        // 如果提供了角色则验证其合法性
        if (spaceRole != null && SpaceRoleEnum.getEnumByValue(spaceRole) == null) {
            throw new BizException(ErrorCode.PARAMS_ERROR, "空间角色不存在");
        }
    }

    /**
     * 获取空间成员响应 DTO
     *
     * @param spaceUserDO 空间成员实体
     * @return 空间成员响应 DTO
     */
    @Override
    public SpaceUserRespDTO getSpaceUserRespDTO(SpaceUserDO spaceUserDO) {
        // 转换基础信息
        SpaceUserRespDTO respDTO = SpaceUserRespDTO.obj2RespDTO(spaceUserDO);

        // 设置用户信息
        respDTO.setUser(getUserInfo(spaceUserDO.getUserId()));

        // 设置空间信息
        respDTO.setSpace(getSpaceInfo(spaceUserDO.getSpaceId()));

        return respDTO;
    }


    /**
     * 获取用户信息
     *
     * @param userId 用户 ID
     * @return 用户响应 DTO
     */
    private UserRespDTO getUserInfo(Long userId) {
        // 获取用户DO并转换为RespDTO
        return userService.getUser(userService.getById(userId));
    }

    /**
     * 获取空间信息
     *
     * @param spaceId 空间 ID
     * @return 空间响应 DTO
     */
    private SpaceRespDTO getSpaceInfo(Long spaceId) {
        // 获取空间DO
        SpaceDO spaceDO = spaceService.getById(spaceId);

        // 转换为RespDTO或返回null
        return spaceDO != null ? SpaceRespDTO.obj2RespDTO(spaceDO) : null;
    }


    /**
     * 批量获取空间成员响应 DTO 列表
     *
     * @param spaceUserDOList 空间成员实体列表
     * @return 空间成员响应 DTO 列表
     */
    @Override
    public List<SpaceUserRespDTO> getSpaceUserRespDTOList(List<SpaceUserDO> spaceUserDOList) {
        // 空集合快速返回
        if (CollUtil.isEmpty(spaceUserDOList)) {
            return Collections.emptyList();
        }

        // 提取所有用户ID和空间ID
        Set<Long> userIds = spaceUserDOList.stream()
                .map(SpaceUserDO::getUserId)
                .collect(Collectors.toSet());
        Set<Long> spaceIds = spaceUserDOList.stream()
                .map(SpaceUserDO::getSpaceId)
                .collect(Collectors.toSet());

        // 批量查询用户和空间信息
        Map<Long, UserDO> userMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(UserDO::getId, Function.identity()));
        Map<Long, SpaceDO> spaceMap = spaceService.listByIds(spaceIds).stream()
                .collect(Collectors.toMap(SpaceDO::getId, Function.identity()));

        // 转换为RespDTO列表
        return spaceUserDOList.stream()
                .map(spaceUser -> {
                    // 转换基础信息
                    SpaceUserRespDTO dto = SpaceUserRespDTO.obj2RespDTO(spaceUser);

                    // 设置用户信息
                    dto.setUser(userService.getUser(userMap.get(spaceUser.getUserId())));

                    // 设置空间信息
                    dto.setSpace(SpaceRespDTO.obj2RespDTO(spaceMap.get(spaceUser.getSpaceId())));

                    return dto;
                })
                .collect(Collectors.toList());
    }

    /**
     * 构建空间成员查询条件
     *
     * @param reqDTO 查询请求 DTO
     * @return 查询条件包装器
     */
    @Override
    public QueryWrapper<SpaceUserDO> buildSpaceUserQueryWrapper(GetSpaceUserReqDTO reqDTO) {
        // 创建基础查询条件
        QueryWrapper<SpaceUserDO> wrapper = new QueryWrapper<>();

        // 空请求直接返回
        if (reqDTO == null) return wrapper;

        // 动态添加查询条件
        wrapper.eq(ObjUtil.isNotEmpty(reqDTO.getId()), "id", reqDTO.getId())
                .eq(ObjUtil.isNotEmpty(reqDTO.getSpaceId()), "space_id", reqDTO.getSpaceId())
                .eq(ObjUtil.isNotEmpty(reqDTO.getUserId()), "user_id", reqDTO.getUserId())
                .eq(ObjUtil.isNotEmpty(reqDTO.getSpaceRole()), "space_role", reqDTO.getSpaceRole());

        return wrapper;
    }

    /**
     * 删除空间成员
     *
     * @param deleteSpaceUserReqDTO 删除成员请求 DTO
     * @return 操作结果
     */
    @Override
    public Response<Boolean> deleteSpaceUser(DeleteSpaceUserReqDTO deleteSpaceUserReqDTO) {
        // 获取要删除的成员信息
        SpaceUserDO spaceUserDO = this.getById(deleteSpaceUserReqDTO.getId());

        // 校验成员是否存在
        ExceptionTrowUtils.throwIf(spaceUserDO == null,
                ErrorCode.NOT_FOUND_ERROR, "被移除成员不存在");

        // 校验不能删除管理员
        ExceptionTrowUtils.throwIf(spaceUserDO.getSpaceRole().equals(SpaceRoleEnum.ADMIN.getValue()),
                ErrorCode.OPERATION_ERROR, "空间管理员不可被移除");

        // 执行删除操作
        boolean result = this.removeById(spaceUserDO.getId());

        // 异步更新空间成员计数
        taskExecutor.execute(() -> {
            boolean update = spaceService.lambdaUpdate()
                    .eq(SpaceDO::getId, spaceUserDO.getSpaceId())
                    .setSql("member_count = member_count - 1")
                    .update();
            ExceptionTrowUtils.throwIf(!update, ErrorCode.OPERATION_ERROR);
            log.info("## 异步更新团队成员数目-1完成 ##");
        });

        return Response.success(result);
    }

    /**
     * 编辑空间成员信息
     *
     * @param reqDTO 编辑成员请求 DTO
     * @return 操作结果
     */
    @Override
    public Response<Boolean> editSpaceUser(EditSpaceUserReqDTO reqDTO) {
        // 转换DTO为DO
        SpaceUserDO spaceUserDO = new SpaceUserDO();
        BeanUtils.copyProperties(reqDTO, spaceUserDO);

        // 获取原始数据
        SpaceUserDO oldData = this.getById(reqDTO.getId());
        ExceptionTrowUtils.throwIf(oldData == null, ErrorCode.NOT_FOUND_ERROR);

        // 验证成员信息
        validSpaceUser(spaceUserDO, false);

        // 检查空间变更
        Long newSpaceId = spaceUserDO.getSpaceId();
        if (newSpaceId != null && !newSpaceId.equals(oldData.getSpaceId())) {
            ExceptionTrowUtils.throwIf(!spaceService.exist(newSpaceId), ErrorCode.NOT_FOUND_ERROR, "空间不存在");
        }

        // 检查用户变更
        Long newUserId = spaceUserDO.getUserId();
        if (newUserId != null && !newUserId.equals(oldData.getUserId())) {
            ExceptionTrowUtils.throwIf(!userService.exist(newUserId), ErrorCode.NOT_FOUND_ERROR, "用户不存在");
        }

        // 校验角色变更
        String newSpaceRole = spaceUserDO.getSpaceRole();

        ExceptionTrowUtils.throwIf(newSpaceRole.equals(SpaceRoleEnum.ADMIN.getValue()),
                ErrorCode.PARAMS_ERROR, "仅可设置一位管理员");
        ExceptionTrowUtils.throwIf(oldData.getSpaceRole().equals(SpaceRoleEnum.ADMIN.getValue()),
                ErrorCode.PARAMS_ERROR, "管理员不可被变更");


        // 执行更新操作
        boolean result = this.updateById(spaceUserDO);
        ExceptionTrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR);

        return Response.success(true);
    }
}